package com.apes.oap.security;

import com.apes.oap.*;
import com.apes.oap.annotation.HttpAction;
import com.apes.oap.config.SystemParameterNames;
import com.apes.oap.impl.DefaultServiceAccessController;
import com.apes.oap.impl.SimpleRopRequestContext;
import com.apes.oap.message.MainMessages;
import com.apes.oap.message.MessageType;
import com.apes.oap.response.OapResponse;
import com.apes.oap.session.SessionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DefaultSecurityManager implements SecurityManager {

    protected final Log logger = LogFactory.getLog(this.getClass());

    protected ServiceAccessController serviceAccessController = new DefaultServiceAccessController();

    protected AppSecretManager appSecretManager = new FileBaseAppSecretManager();

    protected SessionManager sessionManager;

    protected InvokeTimesController invokeTimesController;

    protected FileUploadController fileUploadController;


    public OapResponse<String> validateSystemParameters(RopRequestContext context) {
        RopContext ropContext = context.getRopContext();
        OapResponse<String> oapError = null;

        // 1.检查appKey
        if (context.getAppKey() == null) {
            return MainMessages.get(MessageType.MISSING_APP_KEY, context.getLocale(), context.getMethod(), context.getVersion(), SystemParameterNames.getAppKey());
        }
        if (!appSecretManager.isValidAppKey(context.getAppKey())) {
            return MainMessages.get(MessageType.INVALID_APP_KEY, context.getLocale(), context.getMethod(), context.getVersion(), context.getAppKey());
        }

        // 2.检查会话
        oapError = checkSession(context);
        if (oapError != null) {
            return oapError;
        }

        // 3.检查method参数
        if (context.getMethod() == null) {
            return MainMessages.get(MessageType.MISSING_METHOD, context.getLocale(), SystemParameterNames.getMethod());
        } else {
            if (!ropContext.isValidMethod(context.getMethod())) {
                return MainMessages.get(MessageType.INVALID_METHOD, context.getLocale(), context.getMethod());
            }
        }

        // 4.检查v参数
        if (context.getVersion() == null) {
            return MainMessages.get(MessageType.MISSING_VERSION, context.getLocale(), context.getMethod(), SystemParameterNames.getVersion());
        } else {
            if (!ropContext.isValidVersion(context.getMethod(), context.getVersion())) {
                return MainMessages.get(MessageType.UNSUPPORTED_VERSION, context.getLocale(), context.getMethod(), context.getVersion());
            }
        }

        // 5.检查签名正确性
        oapError = checkSign(context);
        if (oapError != null) {
            return oapError;
        }

        // 6.检查服务方法的版本是否已经过期
        if (context.getServiceMethodDefinition().isObsoleted()) {
            return MainMessages.get(MessageType.METHOD_OBSOLETED, context.getLocale(), context.getMethod(), context.getVersion());
        }

        // 7.检查请求HTTP方法的匹配性
        oapError = validateHttpAction(context);
        if (oapError != null) {
            return oapError;
        }

        return null;
    }

    public OapResponse<String> validateOther(RopRequestContext rrctx) {

        // 1.判断应用/用户是否有权访问目标服务
        OapResponse<String> response = checkServiceAccessAllow(rrctx);
        if (response != null) {
            return response;
        }

        // 2.判断应用/会话/用户访问服务的次数或频度是否超限
        response = checkInvokeTimesLimit(rrctx);
        if (response != null) {
            return response;
        }

        // 3.如果是上传文件的服务，检查文件类型和大小是否满足要求
        response = checkUploadFile(rrctx);
        if (response != null) {
            return response;
        }

        // 4.检查业务参数合法性
        response = validateBusinessParams(rrctx);
        if (response != null) {
            return response;
        }

        return null;
    }

    private OapResponse<String> checkUploadFile(RopRequestContext rrctx) {
        ServiceMethodHandler serviceMethodHandler = rrctx.getServiceMethodHandler();
        if (serviceMethodHandler != null && serviceMethodHandler.hasUploadFiles()) {
            List<String> fileFieldNames = serviceMethodHandler.getUploadFileFieldNames();
            for (String fileFieldName : fileFieldNames) {
                String paramValue = rrctx.getParamValue(fileFieldName);
                if (paramValue != null) {
                }
            }
        }
        return null;
    }

    public void setInvokeTimesController(InvokeTimesController invokeTimesController) {
        this.invokeTimesController = invokeTimesController;
    }

    public void setServiceAccessController(ServiceAccessController serviceAccessController) {
        this.serviceAccessController = serviceAccessController;
    }

    public void setAppSecretManager(AppSecretManager appSecretManager) {
        this.appSecretManager = appSecretManager;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public void setFileUploadController(FileUploadController fileUploadController) {
        this.fileUploadController = fileUploadController;
    }

    private OapResponse<String> checkInvokeTimesLimit(RopRequestContext rrctx) {
        if (invokeTimesController.isAppInvokeFrequencyExceed(rrctx.getAppKey())) {
            return MainMessages.get(MessageType.EXCEED_APP_INVOKE_FREQUENCY_LIMITED, rrctx.getLocale());
        } else if (invokeTimesController.isAppInvokeLimitExceed(rrctx.getAppKey())) {
            return MainMessages.get(MessageType.EXCEED_APP_INVOKE_LIMITED, rrctx.getLocale());
        } else if (invokeTimesController.isSessionInvokeLimitExceed(rrctx.getAppKey(), rrctx.getSessionId())) {
            return MainMessages.get(MessageType.EXCEED_SESSION_INVOKE_LIMITED, rrctx.getLocale());
        } else if (invokeTimesController.isUserInvokeLimitExceed(rrctx.getAppKey(), rrctx.getSession())) {
            return MainMessages.get(MessageType.EXCEED_USER_INVOKE_LIMITED, rrctx.getLocale());
        } else {
            return null;
        }
    }

    /**
     * 校验是否是合法的HTTP动作
     *
     * @param ropRequestContext
     */
    private OapResponse<String> validateHttpAction(RopRequestContext ropRequestContext) {
        OapResponse<String> response = null;
        HttpAction[] httpActions = ropRequestContext.getServiceMethodDefinition().getHttpAction();
        if (httpActions.length > 0) {
            boolean isValid = false;
            for (HttpAction httpAction : httpActions) {
                if (httpAction == ropRequestContext.getHttpAction()) {
                    isValid = true;
                    break;
                }
            }
            if (!isValid) {
                response = MainMessages.get(MessageType.HTTP_ACTION_NOT_ALLOWED, ropRequestContext.getLocale(), ropRequestContext.getMethod(), ropRequestContext.getVersion(),
                        ropRequestContext.getHttpAction());
            }
        }
        return response;
    }

    public ServiceAccessController getServiceAccessController() {
        return serviceAccessController;
    }

    public AppSecretManager getAppSecretManager() {
        return appSecretManager;
    }

    private OapResponse<String> checkServiceAccessAllow(RopRequestContext smc) {
        if (!getServiceAccessController().isAppGranted(smc.getAppKey(), smc.getMethod(), smc.getVersion())) {
            OapResponse<String> oapError = MainMessages.get(MessageType.ISV_INVALID_PERMISSION, smc.getLocale());
            if (oapError != null && logger.isDebugEnabled()) {
                logger.debug("未向ISV开放该服务的执行权限(" + smc.getMethod() + ")");
            }
            return oapError;
        } else {
            if (!getServiceAccessController().isUserGranted(smc.getSession(), smc.getMethod(), smc.getVersion())) {
                OapResponse<String> oapError = MainMessages.get(MessageType.INSUFFICIENT_USER_PERMISSIONS, smc.getLocale(), smc.getMethod(), smc.getVersion());
                if (oapError != null && logger.isDebugEnabled()) {
                    logger.debug("未向会话用户开放该服务的执行权限(" + smc.getMethod() + ")");
                }
                return oapError;
            }
            return null;
        }
    }

    private OapResponse<String> validateBusinessParams(RopRequestContext context) {
        List<ObjectError> errorList = (List<ObjectError>) context.getAttribute(SimpleRopRequestContext.SPRING_VALIDATE_ERROR_ATTRNAME);

        // 将Bean数据绑定时产生的错误转换为Rop的错误
        if (errorList != null && errorList.size() > 0) {
            OapResponse<String> oapResponse = MainMessages.get(MessageType.ISV_INVALID_PARAMETE, context.getLocale());
            String subBody = oapResponse.getBody() + ",";
            for (ObjectError objectError : errorList) {
                if (objectError instanceof FieldError) {
                    subBody += ((FieldError) objectError).getField() + objectError.getDefaultMessage() + ",";
                }
            }
            oapResponse.setBody(subBody);
            return oapResponse;
        } else {
            return null;
        }
    }

    /**
     * 检查签名的有效性
     *
     * @param context
     * @return
     */
    private OapResponse<String> checkSign(RopRequestContext context) {
        // 系统级签名开启,且服务方法需求签名
        if (context.isSignEnable()) {
            if (!context.getServiceMethodDefinition().isIgnoreSign()) {
                if (context.getSign() == null) {
                    return MainMessages.get(MessageType.MISSING_SIGNATURE, context.getLocale(), context.getMethod(), context.getVersion(), SystemParameterNames.getSign());
                } else {

                    // 获取需要签名的参数
                    List<String> ignoreSignFieldNames = context.getServiceMethodHandler().getIgnoreSignFieldNames();
                    HashMap<String, String> needSignParams = new HashMap<String, String>();
                    for (Map.Entry<String, String> entry : context.getAllParams().entrySet()) {
                        if (!ignoreSignFieldNames.contains(entry.getKey())) {
                            needSignParams.put(entry.getKey(), entry.getValue());
                        }
                    }

                    // 查看密钥是否存在，不存在则说明appKey是非法的
                    String signSecret = getAppSecretManager().getSecret(context.getAppKey());
                    if (signSecret == null) {
                        throw new RopException("无法获取" + context.getAppKey() + "对应的密钥");
                    }

                    String signValue = RopUtils.sign(needSignParams, signSecret);
                    System.out.println(signValue);
                    if (!signValue.equals(context.getSign())) {
                        logger.error(context.getAppKey() + "的签名不合法，请检查");
                        return MainMessages.get(MessageType.INVALID_SIGNATURE, context.getLocale(), context.getMethod(), context.getVersion());
                    } else {
                        return null;
                    }
                }
            } else {
                logger.warn(context.getMethod() + "忽略了签名");
                return null;
            }
        } else {
            logger.warn(context.getMethod() + context.getVersion() + "服务方法未开启签名");
            return null;
        }
    }

    /**
     * 是否是合法的会话
     *
     * @param context
     * @return
     */
    private OapResponse<String> checkSession(RopRequestContext context) {
        // 需要进行session检查
        if (context.getServiceMethodHandler() != null && context.getServiceMethodHandler().getServiceMethodDefinition().isNeedInSession()) {
            if (context.getSessionId() == null) {
                return MainMessages.get(MessageType.MISSING_SESSION, context.getLocale(), context.getMethod(), context.getVersion(), SystemParameterNames.getSessionId());
            } else {
                if (!isValidSession(context)) {
                    return MainMessages.get(MessageType.INVALID_SESSION, context.getLocale(), context.getSessionId());
                }
            }
        }
        return null;
    }

    private boolean isValidSession(RopRequestContext smc) {
        if (sessionManager.getSession(smc.getSessionId()) == null) {
            if (logger.isDebugEnabled()) {
                logger.debug(smc.getSessionId() + "会话不存在，请检查。");
            }
            return false;
        } else {
            return true;
        }
    }

}
